## GANET V2
## Copyright (c) 2014 Ganet Development Team. All rights reserved.
##
## This file is subject to the terms and conditions defined in file
## 'LICENSE.md', which is part of this source code package.
## See 'AUTHORS' file for a list of contributors.

import random, heapq
from proxy import UdpProxy

__APP_NAME__ = 'UDP SCRAMBLER'
__COPYRIGHT__ = 'Copyright (c) 2015 Ganet Development Team. All rights reserved.'

class Scrambler(UdpProxy):
    
    def __init__(self, remote_addr, port = None, buffer_size = 4096):
        UdpProxy.__init__(self, remote_addr, port, buffer_size)
        self.loss = 0.0     # percentage (0 to 1)
        self.delay = 0.0    # delay in seconds   
        self.pdv = 0.0      # percentage of delay

    def send(self, socket, addr, msg):
        if random.uniform(0, 1) <= self.loss:
            self._log_debug('embezzled message to %s:%d' % addr)
            return

        if random.uniform(0, 1) <= self.doubling:
            self._log_debug('duplicating message to %s:%d' % addr)
            n = 2
        else:
            n = 1

        for i in range(n):
            delay = self.delay
            ipdv = self.pdv * self.delay
            delay = max(0, delay - ipdv + ipdv * random.uniform(0, 2))
            self._log_debug('delaying message by %d ms' % int(delay * 1000))
            heapq.heappush(self._queue, (delay + self._time, (socket, addr, msg)))

    def run(self):
        self._time = 0
        self._queue = []
        UdpProxy.run(self)

    def update(self, dt):
        self._time += dt

        while self._queue and self._time >= self._queue[0][0]:
            item = heapq.heappop(self._queue)
            UdpProxy.send(self,
                          socket = item[1][0],
                          addr = item[1][1],
                          msg = item[1][2])                       

def _print_header():
    print __APP_NAME__
    print __COPYRIGHT__
    print

def _print_config(scrambler):
    print 'Forwarding packets to \'%s:%d\'' % scrambler.remove_addr
    print 'Listening for packets on port %d' % scrambler.port
    print 'Embezzling packets with %.1f%% probability' % (scrambler.loss * 100)
    print 'Delaying packets by %d ms' % int(scrambler.delay * 1000)
    print 'Packet Delay Variation (jitter) %.1f%%' % (scrambler.pdv * 100)
    print 'Packets are duplicated with %.1f%% probability' % (scrambler.doubling * 100)
     
if __name__ == '__main__':

    remote_addr = ('quantumreboot.com', 54321)
    remote_addr = ('127.0.0.1', 54321)
    port = 12345
    scrambler = Scrambler(remote_addr, port)
    scrambler.loss = 0.5
    scrambler.delay = 0.0
    scrambler.pdv = 0.0
    scrambler.doubling = 0.0

    _print_header()
    _print_config(scrambler)

    scrambler.run()
    print 'scrambler terminated'
